/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

public abstract class StateSync extends AbstractSync 
{
	protected StateSync(Object[] initialState)
	{
		_values = initialState;
		//		_revertMethods = new RevertMethod[_values.length];
	}

	protected Object[] _values;

	//private List<RevertMethod[]> _revertMethods = new LinkedList<RevertMethod[]>();
	protected Stack<RevertMethod[]> _revertMethods = new Stack<RevertMethod[]>();

	protected static abstract class RevertMethod
	{
		public abstract Object revert(Object current);
		public abstract Object cancelRevertPoint(Object current);
	}

	private static class ValueReplacementMethod extends RevertMethod
	{
		Object _value;

		public ValueReplacementMethod(Object value)
		{
			super();
			_value = value;
		}

		@Override
		public Object revert(Object current)
		{
			return _value;
		}

		@Override
		public Object cancelRevertPoint(Object current)
		{
			return current;
		}
		
		
	}

	private static class InverseModMethod extends RevertMethod
	{
		Mod _inverseMod;

		public InverseModMethod(Mod inverseMod)
		{
			super();
			_inverseMod = inverseMod;
		}

		public void appendMod(Mod inverseMod)
		{
			_inverseMod = inverseMod.merge(_inverseMod);
		}

		@Override
		public Object revert(Object current)
		{
			return _inverseMod.inPlaceApply(current);
		}
		
		@Override
		public Object cancelRevertPoint(Object current)
		{
			return current;
		}

	}

	public void cancelRevertPoint()
	{
		RevertMethod[] revertMethods = _revertMethods.pop();
		for (int i = 0; i < revertMethods.length; i++)
		{
			if (revertMethods[i] != null)
				_values[i] = revertMethods[i].cancelRevertPoint(_values[i]);
		}
	}

	public void revert()
	{
		RevertMethod[] revertMethods = _revertMethods.pop();
		for (int i = 0; i < revertMethods.length; i++)
		{
			if (revertMethods[i] != null)
				_values[i] = revertMethods[i].revert(_values[i]);
		}
	}

	public void setRevertPoint()
	{
		_revertMethods.push(new RevertMethod[_values.length]);

	}

	protected final Object getAttribute(int index)
	{
		return _values[index];
	}

	protected final void setAttribute(int index, Object value)
	{
		if (!_revertMethods.empty()
				&& _revertMethods.peek()[index] == null)
		{
			_revertMethods.peek()[index] = new ValueReplacementMethod(
					_values[index]);
		}
		else
			assert (_revertMethods.empty() || _revertMethods.peek()[index] instanceof ValueReplacementMethod);
		_values[index] = value;
	}
	
	protected final void modifyAttribute(int index, Mod mod)
	{
		assert mod != null;
		if (!_revertMethods.empty())
		{
			if (_revertMethods.peek()[index] == null)
			{
				_revertMethods.peek()[index] = new InverseModMethod(mod
						.getInverse(_values[index]));
			}
			else
			{
				assert _revertMethods.peek()[index] instanceof InverseModMethod;
				((InverseModMethod) _revertMethods.peek()[index]).appendMod(mod
						.getInverse(_values[index]));
			}
		}
		_values[index] = mod.inPlaceApply(_values[index]);
	}

}
